import { useEffect, useRef, useState, useCallback, type FormEvent } from 'react';
import debounce from 'lodash.debounce';
import { QRCode, message } from 'antd';
import qrcodeParser from 'qrcode-parser';

import Icon from '@/components/Icon';
import Menu from '@/components/Menu';
import Collect from '@/components/Collect';
import { doDownload } from '@/common/utils';
import { getQRCodeUrl } from '@/common/url';
import { pageMode, popupMode } from '@/common/constant';
import { getCollectList, setCollectList, type CollectList } from '@/common/storage';

import styles from './index.module.scss';

/** 菜单ID */
const MENU_ID = 'qrcode_canvas';

export default function Home() {
  // 二维码的值
  const [qrcodeValue, setQrcodeValue] = useState('404');
  // 显示收藏
  const [showCollect, setShowCollect] = useState(false);
  // 收藏初始化
  const [collectInited, setCollectInited] = useState(pageMode);
  // 收藏数据
  const [collectData, setCollectData] = useState<CollectList>([]);

  /** 右击菜单 */
  const menuRef = useRef<any>(null);
  /** 上一次手动输入值 */
  const prevInputValue = useRef('');
  /** 编辑项的索引 */
  const editIndexRef = useRef<number | null>(null);
  const textareaRef = useRef<HTMLTextAreaElement>(null);

  const [messageApi, contextHolder] = message.useMessage();

  useEffect(() => {
    // 初始化二维码
    initQRCode();
    // 获取收藏数据
    getCollect();
    // 读取剪切板二维码内容
    document.addEventListener('paste', handlePaste);
    return () => document.removeEventListener('paste', handlePaste);
  }, []);

  /**
   * 获取收藏数据
   */
  const getCollect = useCallback(async () => {
    const data = await getCollectList();
    setCollectData(data);
  }, []);

  /**
   * 更新收藏
   */
  const updateCollect = useCallback((data: CollectList) => {
    setCollectData(data);
    setCollectList(data);
  }, []);

  /**
   * 聚焦到最后一个字符
   */
  const focusLastChar = useCallback((value = '') => {
    const textarea = textareaRef.current;
    if (textarea) {
      textarea.focus();
      const index = (value || textarea.value).length;
      textarea.setSelectionRange(index, index);
    }
  }, []);

  /**
   * 设置输入框内容
   */
  const setValue = useCallback(
    (value = '', isDisabled = false) => {
      textareaRef.current!.value = value;
      if (value) setQrcodeValue(value);
      if (!isDisabled) focusLastChar(value);
    },
    [focusLastChar],
  );

  /**
   * 读取剪切板二维码内容
   */
  const handlePaste = useCallback(
    (e: ClipboardEvent) => {
      const clipboard = e.clipboardData;
      if (!clipboard) return;
      // 获取粘贴的内容
      const file = clipboard.files[0];
      // 如果没有文件或文件不是图片，则退出
      if (!file) return;
      if (!/^image\/[jpeg|png|gif|jpg]/.test(file.type)) {
        messageApi.error('只支持图片格式');
        return;
      }
      textareaRef.current!.blur();
      qrcodeParser(file)
        .then(setValue)
        .catch(() => setValue('识别二维码失败，请确认二维码是否正确！'));
    },
    [messageApi, setValue],
  );

  /**
   * 初始化二维码
   */
  const initQRCode = useCallback(
    async (url = '', isDisabled = false) => {
      let qrcode = url;
      if (!url) qrcode = await getQRCodeUrl();
      setValue(qrcode, isDisabled);
    },
    [setValue],
  );

  /**
   * 设置输入框内容
   */
  const debounceInput = useCallback(
    debounce((e: FormEvent<HTMLTextAreaElement>) => {
      const { value = '' } = e.target as HTMLTextAreaElement;
      if (value) setQrcodeValue(value);
      const editIndex = editIndexRef.current;
      // 编辑态，自动保存
      if (typeof editIndex === 'number') {
        collectData[editIndex].url = value;
        updateCollect(collectData.slice());
      } else {
        // 只对未编辑态存档
        prevInputValue.current = value;
      }
    }, 300),
    [collectData, updateCollect],
  );

  /**
   * 显示收藏浮层
   */
  const showCollectPopup = useCallback(() => {
    focusLastChar();
    setCollectInited(true);
    setShowCollect(!showCollect);
  }, [showCollect, focusLastChar]);

  /**
   * 编辑或选择改变
   */
  const handleAction = useCallback(
    (url?: string, index?: number) => {
      if (popupMode) showCollectPopup();
      const isEdit = typeof index === 'number';
      const isDisabled = !isEdit && Boolean(url);
      textareaRef.current!.disabled = isDisabled;
      editIndexRef.current = isEdit ? index : null;
      initQRCode(url || prevInputValue.current, isDisabled);
    },
    [initQRCode, showCollectPopup],
  );

  /**
   * 二维码右击
   */
  const handleContextMenu = useCallback((event) => menuRef.current!.show({ event }), []);

  /**
   * 处理菜单选择的共同逻辑
   */
  const handleMenu = useCallback((func: (canvas: HTMLCanvasElement) => void) => {
    return () => {
      const canvas = document.querySelector('canvas');
      if (canvas) func(canvas);
    };
  }, []);

  /**
   * 二维码右击 - 复制
   */
  const handleCopy = useCallback(
    handleMenu((canvas) => {
      canvas.toBlob(async (blob) => {
        if (blob) {
          try {
            const item = new ClipboardItem({ 'image/png': blob });
            await navigator.clipboard.write([item]);
            messageApi.success('复制成功');
          } catch (err) {
            messageApi.error('复制失败');
          }
        }
      }, 'image/png');
    }),
    [],
  );

  /**
   * 二维码右击 - 下载
   */
  const handleDownload = useCallback(
    handleMenu((canvas) => {
      const url = canvas.toDataURL();
      doDownload(url, 'QRCode.png');
      messageApi.success('下载成功');
    }),
    [doDownload],
  );

  return (
    <>
      <div x-class={[styles.home, { [styles.pageMode]: pageMode }]}>
        <div x-class={[styles.block, { [styles.showCollect]: showCollect }]}>
          <div className={styles.container}>
            <QRCode
              className={styles.qrcode}
              size={250}
              color="#1e4150"
              bordered={false}
              value={qrcodeValue}
              onContextMenu={handleContextMenu}
            />
            <textarea className={styles.edit} ref={textareaRef} onInput={debounceInput} />
          </div>
          <Collect
            x-if={collectInited}
            className={styles.collect}
            collectData={collectData}
            textareaRef={textareaRef}
            handleAction={handleAction}
            updateCollect={updateCollect}
          />
        </div>
        <div x-if={popupMode} className={styles.handleWrap} onClick={showCollectPopup}>
          <Icon />
        </div>
      </div>

      <Menu
        ref={menuRef}
        id={MENU_ID}
        items={[
          { icon: 'copy', name: '复制', onClick: handleCopy },
          { icon: 'download', name: '下载', onClick: handleDownload },
        ]}
      />

      {contextHolder}
    </>
  );
}
